package com.boarsoft.dao.sharding;

import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.Query;
import javax.persistence.SynchronizationType;
import javax.persistence.criteria.CriteriaBuilder;

import org.hibernate.Cache;
import org.hibernate.HibernateException;
import org.hibernate.Metamodel;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.StatelessSessionBuilder;
import org.hibernate.TypeHelper;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.stat.Statistics;

import com.boarsoft.dao.sharding.bean.ShardingParam;
import com.boarsoft.dao.sharding.datasource.DynamicDataSource;

@SuppressWarnings({ "rawtypes", "deprecation" })
public class HibernateShardedSessionFactory implements SessionFactory {
	private static final long serialVersionUID = 2231397539984281263L;

	protected SessionFactory sessionFactory;

	@Override
	public Session getCurrentSession() throws HibernateException {
		ShardingParam sp = DynamicDataSource.getBinding();
		Session ss = null;
		// 如果没有绑定分片参数，则不需要分片处理
		if (sp == null) {
			try {
				return sessionFactory.getCurrentSession();
			} catch (Exception e) {
				return sessionFactory.openSession();
			}
		}
		// 如果存在嵌套（当前线程已绑定session），则使用当前的session
		ss = (Session) sp.getSession();
		// 非嵌套的分片方法，或者被嵌套的方法希望定位到别的分片时，
		// DaoShardingAdviceImpl总是创建新的ShardingParam，其session总是null
		if (ss == null) {
			ss = this.openSession(); // 开启新session
			sp.setSession(ss);
		}
		return ss;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	// -------------------

	public Reference getReference() throws NamingException {
		return sessionFactory.getReference();
	}

	@Override
	public SessionFactoryOptions getSessionFactoryOptions() {
		return sessionFactory.getSessionFactoryOptions();
	}

	@Override
	public SessionBuilder withOptions() {
		return sessionFactory.withOptions();
	}

	@Override
	public Session openSession() throws HibernateException {
		return sessionFactory.openSession();
	}

	@Override
	public StatelessSessionBuilder withStatelessOptions() {
		return sessionFactory.withStatelessOptions();
	}

	@Override
	public StatelessSession openStatelessSession() {
		return sessionFactory.openStatelessSession();
	}

	@Override
	public StatelessSession openStatelessSession(Connection connection) {
		return sessionFactory.openStatelessSession(connection);
	}

	@Override
	public ClassMetadata getClassMetadata(Class entityClass) {
		return sessionFactory.getClassMetadata(entityClass);
	}

	@Override
	public ClassMetadata getClassMetadata(String entityName) {
		return sessionFactory.getClassMetadata(entityName);
	}

	@Override
	public CollectionMetadata getCollectionMetadata(String roleName) {
		return sessionFactory.getCollectionMetadata(roleName);
	}

	@Override
	public Map<String, ClassMetadata> getAllClassMetadata() {
		return sessionFactory.getAllClassMetadata();
	}

	@Override
	public Map getAllCollectionMetadata() {
		return sessionFactory.getAllClassMetadata();
	}

	@Override
	public Statistics getStatistics() {
		return sessionFactory.getStatistics();
	}

	@Override
	public void close() throws HibernateException {
		sessionFactory.close();
	}

	@Override
	public boolean isClosed() {
		return sessionFactory.isClosed();
	}

	@Override
	public Cache getCache() {
		return sessionFactory.getCache();
	}

	@Override
	public Set getDefinedFilterNames() {
		return sessionFactory.getDefinedFilterNames();
	}

	@Override
	public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
		return sessionFactory.getFilterDefinition(filterName);
	}

	@Override
	public boolean containsFetchProfileDefinition(String name) {
		return sessionFactory.containsFetchProfileDefinition(name);
	}

	@Override
	public TypeHelper getTypeHelper() {
		return sessionFactory.getTypeHelper();
	}

	@Override
	public EntityManager createEntityManager() {
		return sessionFactory.createEntityManager();
	}

	@Override
	public EntityManager createEntityManager(Map map) {
		return sessionFactory.createEntityManager(map);
	}

	@Override
	public EntityManager createEntityManager(SynchronizationType synchronizationType) {
		return sessionFactory.createEntityManager(synchronizationType);
	}

	@Override
	public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {
		return sessionFactory.createEntityManager(synchronizationType, map);
	}

	@Override
	public CriteriaBuilder getCriteriaBuilder() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isOpen() {
		return sessionFactory.isOpen();
	}

	@Override
	public Map<String, Object> getProperties() {
		return sessionFactory.getProperties();
	}

	@Override
	public PersistenceUnitUtil getPersistenceUnitUtil() {
		return sessionFactory.getPersistenceUnitUtil();
	}

	@Override
	public void addNamedQuery(String name, Query query) {
		sessionFactory.addNamedQuery(name, query);
	}

	@Override
	public <T> T unwrap(Class<T> cls) {
		return sessionFactory.unwrap(cls);
	}

	@Override
	public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph) {
		sessionFactory.addNamedEntityGraph(graphName, entityGraph);
	}

	@Override
	public <T> List<EntityGraph<? super T>> findEntityGraphsByType(Class<T> entityClass) {
		return sessionFactory.findEntityGraphsByType(entityClass);
	}

	@Override
	public Metamodel getMetamodel() {
		return sessionFactory.getMetamodel();
	}
}
